defmodule Mix.Tasks.Compile.SurfaceTest do
  use ExUnit.Case, async: false
  import ExUnit.CaptureIO

  import Mix.Tasks.Compile.Surface
  import ExUnit.CaptureIO

  alias Mix.Task.Compiler.Diagnostic

  @hooks_rel_output_dir "tmp/_hooks"
  @hooks_output_dir Path.join(File.cwd!(), @hooks_rel_output_dir)

  @hooks_index_file Path.join(@hooks_output_dir, "index.js")

  @css_rel_output_file "tmp/_components.css"
  @css_output_file Path.join(File.cwd!(), @css_rel_output_file)

  setup_all do
    conf_before = Application.get_env(:surface, :compiler, [])

    Application.put_env(:surface, :compiler,
      hooks_output_dir: @hooks_rel_output_dir,
      css_output_file: @css_rel_output_file
    )

    on_exit(fn ->
      Application.put_env(:surface, :compiler, conf_before)
    end)

    :ok
  end

  setup do
    if File.exists?(@hooks_output_dir) do
      File.rm_rf!(@hooks_output_dir)
    end

    if File.exists?(@css_output_file) do
      File.rm_rf!(@css_output_file)
    end

    on_exit(fn ->
      File.rm_rf!(@hooks_output_dir)
      File.rm_rf!(@css_output_file)
    end)

    :ok
  end

  test "do nothing when receivning --from-mix-deps-compile" do
    assert run(["--from-mix-deps-compile"]) == {:noop, []}
  end

  test "generate index.js with empty object if there's no hooks available" do
    refute File.exists?(@hooks_output_dir)

    assert capture_io(:standard_error, fn -> run(["--return-errors"]) end) == ""

    assert File.read!(@hooks_index_file) == """
           /*
           This file was generated by the Surface compiler.
           */

           export default {}
           """
  end

  test "prints and returns `{:ok, diagnostics}` on warning without `return_errors` and `warnings_as_errors`" do
    diagnostic = %Diagnostic{
      message: "test warning",
      file: "file.ex",
      position: 1,
      severity: :warning,
      compiler_name: "Surface"
    }

    output =
      capture_io(:standard_error, fn ->
        assert {:ok, [^diagnostic]} = handle_diagnostics([diagnostic], [])
      end)

    assert output ==
             (IO.ANSI.format([:yellow, "warning: "]) |> IO.iodata_to_binary()) <>
               "test warning\n  file.ex:1: (file)\n\n"
  end

  test "don't print and return `{:error, diagnostics}` on warning with `return_errors` and `warnings_as_errors`" do
    diagnostic = %Diagnostic{
      message: "test warning",
      file: "file.ex",
      position: 1,
      severity: :warning,
      compiler_name: "Surface"
    }

    output =
      capture_io(:standard_error, fn ->
        assert {:error, [^diagnostic]} =
                 handle_diagnostics([diagnostic], return_errors: true, warnings_as_errors: true)
      end)

    refute output =~ "test warning"
  end

  test "prints and returns `{:error, diagnostics}` on error without `return_errors`" do
    diagnostic = %Diagnostic{
      message: "test error",
      file: "file.ex",
      position: 1,
      severity: :error,
      compiler_name: "Surface"
    }

    output =
      capture_io(:standard_error, fn ->
        assert {:error, [^diagnostic]} = handle_diagnostics([diagnostic], [])
      end)

    assert output == (IO.ANSI.format([:red, "error: "]) |> IO.iodata_to_binary()) <> "test error\n  file.ex:1\n"
  end

  test "don't print and return `{:error, diagnostics}` on error with `return_errors`" do
    diagnostic = %Diagnostic{
      message: "test error",
      file: "file.ex",
      position: 1,
      severity: :error,
      compiler_name: "Surface"
    }

    output =
      capture_io(:standard_error, fn ->
        assert {:error, [^diagnostic]} = handle_diagnostics([diagnostic], return_errors: true)
      end)

    assert output == ""
  end
end
